﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.IO;
using PwdMgr.Utility.Exceptions;

namespace PwdMgr.Utility.Crypto
{
    public class CryptoUtil
    {
        /// <summary>
        /// This function will use ASCII to get buffer of str and MD5 it and Convert it to base 64 str.
        /// </summary>
        public static string MD5(string str)
        {
            if (str == null)
                return null;

            byte[] buffer = Encoding.ASCII.GetBytes(str);
            byte[] data = MD5(buffer);
            return Convert.ToBase64String(data);
        }

        public static byte[] MD5(byte[] buffer)
        {
            using (MD5CryptoServiceProvider md5 = 
                new MD5CryptoServiceProvider())
            {
                return md5.ComputeHash(buffer);
            }
        }

        public static byte[] SHA1(byte[] buffer)
        {
            using (SHA1CryptoServiceProvider sha1 = 
                new SHA1CryptoServiceProvider())
            {
                return sha1.ComputeHash(buffer);
            }
        }

        public static byte[] TripleDESEncrypt(
            byte[] key,
            byte[] iv,
            byte[] buffer)
        {
            using (TripleDESCryptoServiceProvider tdes = 
                new TripleDESCryptoServiceProvider())
            {
                using (MemoryStream ms = new MemoryStream())
                {
                    using (CryptoStream cs = new CryptoStream(ms,
                        tdes.CreateEncryptor(key, iv),
                        CryptoStreamMode.Write))
                    {
                        cs.Write(buffer, 0, buffer.Length);
                        cs.Flush();
                    }
                    return ms.ToArray();
                }
            }
        }

        public static byte[] TripleDESDecrypt(
            byte[] key,
            byte[] iv,
            byte[] buffer)
        {
            using (TripleDESCryptoServiceProvider tdes = 
                new TripleDESCryptoServiceProvider())
            {
                using (MemoryStream ms = new MemoryStream(buffer))
                {
                    byte[] data = new byte[buffer.Length];
                    using (CryptoStream cs = new CryptoStream(ms,
                        tdes.CreateDecryptor(key, iv),
                        CryptoStreamMode.Read))
                    {
                        cs.Read(data, 0, data.Length);
                    }
                    return data;
                }
            }
        }

        public static string Encrypt(string plain, string key)
        {
            if (string.IsNullOrEmpty(plain))
                throw new ArgumentNullException();

            if (string.IsNullOrEmpty(key))
                throw new ArgumentNullException();

            try
            {
                byte[] buffer = Encoding.ASCII.GetBytes(plain);
                byte[] keyBuf = Encoding.ASCII.GetBytes(key);

                byte[] valueBuf = Encrypt(buffer, keyBuf);

                return Convert.ToBase64String(valueBuf);
            }
            catch (Exception)
            {
                throw new CryptoException();
            }
        }

        public static string Decrypt(string text, string key)
        {
            if (string.IsNullOrEmpty(text))
                throw new ArgumentNullException();

            if (string.IsNullOrEmpty(key))
                throw new ArgumentNullException();

            try
            {
                byte[] buffer = Convert.FromBase64String(text);
                byte[] keyBuf = Encoding.ASCII.GetBytes(key);

                byte[] plainBuf = Decrypt(buffer, keyBuf);

                return Encoding.ASCII.GetString(plainBuf);
            }
            catch (Exception)
            {
                throw new CryptoException();
            }
        }
        public static byte[] Encrypt(byte[] buffer, byte[] key)
        {
            ThrowUtil.ArgumentNullException(key, "key");
            ThrowUtil.ArgumentNullException(buffer, "buffer");
            try
            {
                byte[] enKey = new byte[24];
                byte[] enIV = new byte[8];
                byte[] md5Hash = MD5(key);
                byte[] sha1Hash = SHA1(key);

                Array.Copy(md5Hash, enKey, 16);
                Array.Copy(sha1Hash, 0, enKey, 16, 8);
                Array.Copy(sha1Hash, 8, enIV, 0, 8);

                return TripleDESEncrypt(enKey, enIV, buffer);
            }
            catch (Exception)
            {
                throw new CryptoException();
            }
        }

        public static byte[] Decrypt(byte[] buffer, byte[] key)
        {
            ThrowUtil.ArgumentNullException(key, "key");
            ThrowUtil.ArgumentNullException(buffer, "buffer");
            try
            {
                byte[] enKey = new byte[24];
                byte[] enIV = new byte[8];
                byte[] md5Hash = MD5(key);
                byte[] sha1Hash = SHA1(key);

                Array.Copy(md5Hash, enKey, 16);
                Array.Copy(sha1Hash, 0, enKey, 16, 8);
                Array.Copy(sha1Hash, 8, enIV, 0, 8);

                return TripleDESDecrypt(enKey, enIV, buffer);
            }
            catch (Exception)
            {
                throw new CryptoException();
            }
        }
    }
}
